{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Jupyter Tipps & Tricks <a class=\"tocSkip\">\n",
    "Add `<a class=\"tocSkip\">` after the notebook after the notebook's main heading to prevent numbering in for the table of content (TOC).\n",
    "    \n",
    "**In this notebook:**\n",
    "\n",
    "* [Shortcuts](#Shortcuts)\n",
    "* [Import Tricks](#Import-Tricks)\n",
    "* [Magic Commands](#Magic-commands)\n",
    "* [Interactive Components](#Interactive-Components)\n",
    "* [Guidelines](#Guidelines)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2017-06-30T12:49:23.824944Z",
     "start_time": "2017-06-30T12:49:23.819621Z"
    }
   },
   "source": [
    "## Shortcuts\n",
    "\n",
    "* <kbd>TAB</kbd>: Auto-completion of identifiers\n",
    "* <kbd>Shift + Enter</kbd>: Auto-completion of parameters\n",
    "* <kbd>CTRL + Enter</kbd>: Execute cell\n",
    "* <kbd>ESC + A/B</kbd>: Insert cell above or below\n",
    "* <kbd>ESC + O</kbd>: Toggle output\n",
    "* <kbd>ESC + M</kbd>: Change cell to markdown\n",
    "* <kbd>ESC + Y</kbd>: Change cell to code\n",
    "* <kbd>Shift + Tab</kbd>: Show docs for selected function\n",
    "* <kbd>Shift + Command + P</kbd>: Functionaltiy search\n",
    "* <kbd>ESC + F</kbd>: Find and replace on your code but not the outputs. \n",
    "* <kbd>H</kbd>: Show all keyboard shortcuts\n",
    "\n",
    "Find more shortcuts [here](https://www.cheatography.com/weidadeyue/cheat-sheets/jupyter-notebook/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import Tricks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Re-import Packages on Change"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When editing imported code, use `%load_ext autoreload; %autoreload 2` . The autoreload utility reloads modules automatically before entering the execution of code typed at the IPython prompt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:04.199307Z",
     "start_time": "2020-01-26T11:20:04.149927Z"
    }
   },
   "outputs": [],
   "source": [
    "# Re-import packages if they change\n",
    "%load_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Install Dependencies\n",
    "To install pip dependencies always in the correct python environment, use the `sys.executable`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:11.524240Z",
     "start_time": "2020-01-26T11:20:05.772867Z"
    }
   },
   "outputs": [],
   "source": [
    "import sys\n",
    "!{sys.executable} -m pip install tqdm  # sys.executable points to the python that is running in your kernel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Show all Logs\n",
    "Many libraries use the python `logging` system instead of `print`. To show all of these logs correctly, you can `basicConfig` to configure the logging:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:13.109631Z",
     "start_time": "2020-01-26T11:20:13.080903Z"
    }
   },
   "outputs": [],
   "source": [
    "import logging, sys\n",
    "\n",
    "# Enable logging\n",
    "logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO, stream=sys.stdout)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Configure Matplotlib for use in Notebooks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:14.602218Z",
     "start_time": "2020-01-26T11:20:14.564950Z"
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Ensure that the plots are rendered inside the notebook\n",
    "%matplotlib inline \n",
    "plt.rcParams[\"figure.figsize\"] = (12,6) # set the default figure size of plots\n",
    "%config InlineBackend.figure_format='retina' # adapt plots for retina displays"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Always use tqdm Notebook Component"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:30:28.507209Z",
     "start_time": "2020-01-26T11:30:28.481671Z"
    }
   },
   "outputs": [],
   "source": [
    "# Intialize tqdm to always use the notebook progress bar\n",
    "import tqdm\n",
    "tqdm.tqdm = tqdm.notebook.tqdm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:30:34.881060Z",
     "start_time": "2020-01-26T11:30:32.097558Z"
    }
   },
   "outputs": [],
   "source": [
    "from time import sleep\n",
    "for i in tqdm.tqdm(range(250)):\n",
    "    sleep(0.01)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Pretty Print all Cell Output\n",
    "Normally only the last output in the cell gets pretty printed - the rest you have to manually add print() which is not very convenient. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:20.868984Z",
     "start_time": "2020-01-26T11:20:20.841058Z"
    }
   },
   "outputs": [],
   "source": [
    "var1 = 4+5\n",
    "var2 = 5+6\n",
    "var1\n",
    "var2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is how to change that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:22.497238Z",
     "start_time": "2020-01-26T11:20:22.473739Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "\n",
    "# pretty print all cell's output and not just the last one\n",
    "InteractiveShell.ast_node_interactivity = \"all\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:23.843849Z",
     "start_time": "2020-01-26T11:20:23.814621Z"
    }
   },
   "outputs": [],
   "source": [
    "var1 = 4+5\n",
    "var2 = 5+6\n",
    "var1\n",
    "var2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To restore the original behavior add:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:27.523113Z",
     "start_time": "2020-01-26T11:20:27.501848Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "\n",
    "# pretty print only the last output of the cell\n",
    "InteractiveShell.ast_node_interactivity = \"last_expr\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-10-30T10:20:45.624235Z",
     "start_time": "2018-10-30T10:20:45.618213Z"
    }
   },
   "source": [
    "Find more information about this trick [here](https://forums.fast.ai/t/jupyter-notebook-enhancements-tips-and-tricks/17064/2)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Magic commands\n",
    "Find all built-in magic commands here: http://ipython.readthedocs.io/en/stable/interactive/magics.html\n",
    "\n",
    "Please don't overdo cell magic, only use if it is very helpful."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Run Bash Commands"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run single-line bash commands"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:30.068361Z",
     "start_time": "2020-01-26T11:20:29.378184Z"
    }
   },
   "outputs": [],
   "source": [
    "!pwd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Run multi-line bash commands"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:31.090898Z",
     "start_time": "2020-01-26T11:20:31.044846Z"
    }
   },
   "outputs": [],
   "source": [
    "%%bash\n",
    "pwd\n",
    "ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Explore Functions & Modules "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:32.885918Z",
     "start_time": "2020-01-26T11:20:32.847409Z"
    }
   },
   "outputs": [],
   "source": [
    "# View function and class docstrings:\n",
    "import numpy as np\n",
    "?np.random.normal()\n",
    "# or\n",
    "np.random.normal??"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:36.718354Z",
     "start_time": "2020-01-26T11:20:36.679650Z"
    }
   },
   "outputs": [],
   "source": [
    "# List all the variables/functions in a module: \n",
    "np.ra*?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Time Measures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:39.505121Z",
     "start_time": "2020-01-26T11:20:39.474407Z"
    }
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "# Time the execution of a cell.\n",
    "import numpy as np\n",
    "np.random.normal(size=50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:48.804906Z",
     "start_time": "2020-01-26T11:20:42.503645Z"
    }
   },
   "outputs": [],
   "source": [
    "# Test average runtime of function in 10k executions\n",
    "%timeit np.random.normal(size=50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:48.824038Z",
     "start_time": "2020-01-26T11:20:48.807439Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Show how much time your program spent in each function\n",
    "%prun np.random.normal(size=50)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Inspections & Debugging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:48.855174Z",
     "start_time": "2020-01-26T11:20:48.827834Z"
    }
   },
   "outputs": [],
   "source": [
    "# List all variables of global scope.\n",
    "%whos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you see an error, you can run `%debug` in a new cell to activate IPython Debugger. Standard keyboard shortcuts such as c for continue, n for next, q for quit apply."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:53.222354Z",
     "start_time": "2020-01-26T11:20:53.198715Z"
    }
   },
   "outputs": [],
   "source": [
    "# Debugging of last exception\n",
    "%debug\n",
    "# Type q to quit the debugger"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use `from IPython.core.debugger import set_trace` to IPython debugger checkpoints, the same wa.y you would for pdb in PyCharm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:20:59.109226Z",
     "start_time": "2020-01-26T11:20:56.931529Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.core.debugger import set_trace\n",
    "\n",
    "def foobar(n):\n",
    "    x = 1337\n",
    "    y = x + n\n",
    "    set_trace() #this one triggers the debugger\n",
    "    return y\n",
    "\n",
    "foobar(3)\n",
    "# Type q to quit the debugger"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Let the notebook save itself"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:01.140861Z",
     "start_time": "2020-01-26T11:21:01.116978Z"
    }
   },
   "outputs": [],
   "source": [
    "%%javascript\n",
    "IPython.notebook.save_notebook()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set Environment Variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:05.234459Z",
     "start_time": "2020-01-26T11:21:05.207775Z"
    }
   },
   "outputs": [],
   "source": [
    "# Running %env without any arguments\n",
    "# lists all environment variables\n",
    "%env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:07.345065Z",
     "start_time": "2020-01-26T11:21:07.316720Z"
    }
   },
   "outputs": [],
   "source": [
    "# The line below sets the environment\n",
    "%env TEST_ENV_VAR=1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interactive Components\n",
    "\n",
    "For interactive data visualization tools, please refer to the [Visualization in Jupyter](./visualization-tutorial.ipynb) tutorial."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TQDM - Progress Bars for Loops\n",
    "Only works in Python 3 currently"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:31:25.697447Z",
     "start_time": "2020-01-26T11:31:22.809532Z"
    }
   },
   "outputs": [],
   "source": [
    "# Show loading bar in notebook\n",
    "import tqdm\n",
    "from time import sleep\n",
    "\n",
    "for i in tqdm.notebook.tqdm(range(250)):\n",
    "    sleep(0.01)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Print markdown output "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:15.580671Z",
     "start_time": "2020-01-26T11:21:15.553252Z"
    }
   },
   "outputs": [],
   "source": [
    "# https://forums.fast.ai/t/jupyter-notebook-enhancements-tips-and-tricks/17064/5\n",
    "from IPython.display import Markdown, display\n",
    "def printmd(string, color=None):\n",
    "    colorstr = \"<span style='color:{}'>{}</span>\".format(color, string)\n",
    "    display(Markdown(colorstr))\n",
    "\n",
    "printmd(\"**bold and blue**\", color=\"blue\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-02-26T14:40:26.891298Z",
     "start_time": "2018-02-26T14:40:26.888752Z"
    }
   },
   "source": [
    "### QGrid - Interactive Grid for Sorting, Filtering, and Editing DataFrames"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:17.652286Z",
     "start_time": "2020-01-26T11:21:17.273467Z"
    }
   },
   "outputs": [],
   "source": [
    "# For more information: https://github.com/quantopian/qgrid\n",
    "\n",
    "import qgrid\n",
    "\n",
    "# Create dataframe with random data\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))\n",
    "\n",
    "# Render interactive dataframe explorer\n",
    "qgrid.show_grid(df, show_toolbar=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-02-26T14:51:16.710893Z",
     "start_time": "2018-02-26T14:51:16.707643Z"
    }
   },
   "source": [
    "### Interactive Jupyter Widgets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:21.739565Z",
     "start_time": "2020-01-26T11:21:21.673348Z"
    }
   },
   "outputs": [],
   "source": [
    "# See more: https://blog.dominodatalab.com/interactive-dashboards-in-jupyter/\n",
    "\n",
    "from ipywidgets import widgets\n",
    "from IPython.display import display\n",
    "\n",
    "text = widgets.Text()\n",
    "display(text)\n",
    "\n",
    "button = widgets.Button(description=\"Click Me!\")\n",
    "display(button)\n",
    "\n",
    "def on_button_click(b):\n",
    "    print(text.value)\n",
    "    \n",
    "button.on_click(on_button_click)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-02-26T19:09:31.381261Z",
     "start_time": "2018-02-26T19:09:31.378227Z"
    }
   },
   "source": [
    "### Render HTML Content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:24.096850Z",
     "start_time": "2020-01-26T11:21:24.061564Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.core.display import display, HTML\n",
    "\n",
    "circle_size = 50\n",
    "\n",
    "# Display any HTML within the output\n",
    "display(HTML('<svg><circle cx=\"'+str(circle_size)+'\" cy=\"'+str(circle_size)+'\" r=\"50\" fill=\"red\" /></svg>'))\n",
    "\n",
    "# you can also embed iframes\n",
    "display(HTML('<iframe width=\"600\" height=\"300\" src=\"../\"></iframe>'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-03-01T00:17:17.674094Z",
     "start_time": "2018-03-01T00:17:17.670353Z"
    }
   },
   "source": [
    "### Run Javascript\n",
    "\n",
    "This is only possible if you're using Jupyter Notebooks because JupyterLab's Javascript API has changed. See [here](https://github.com/jupyterlab/jupyterlab/issues/5888#issuecomment-455790501) for more information. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:28.564017Z",
     "start_time": "2020-01-26T11:21:28.537792Z"
    }
   },
   "outputs": [],
   "source": [
    "%%javascript\n",
    "var notebookUrl = window.location.href \n",
    "\n",
    "// Save notebook URL into a python variable\n",
    "IPython.notebook.kernel.execute('notebook_url=\"' + notebookUrl + '\";');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Combine JavaScript with Python:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-01-26T11:21:30.061484Z",
     "start_time": "2020-01-26T11:21:30.035921Z"
    }
   },
   "outputs": [],
   "source": [
    "# Print notebook url that was captured with javascript\n",
    "print(notebook_url)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-17T12:00:13.177028Z",
     "start_time": "2018-11-17T12:00:13.020263Z"
    }
   },
   "source": [
    "You can find more informaiton about running JavaScript in Jupyter Notebooks [here](http://nbviewer.jupyter.org/github/ipython/ipython/blob/3.x/examples/Notebook/JavaScript%20Notebook%20Extensions.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Guidelines"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- All cells should be executable in order (with run all and restart & run all).\n",
    "- Every notebook should be self-contained and executable without any prior knowledge. \n",
    "- Each cell should have one logical output (idea-execution-output triple).\n",
    "- Frequently rewrite each cell logic into functions. These functions can be moved to separate `.py` files on regular intervals. Your notebook run should be mainly function calls. This would prevent your notebook from becoming a giant pudding of several global variables.\n",
    "- Any code that is used in more than 3 notebooks should be moved to `.py` files (such as utils.py) and imported such as `from xxx_imports import *`\n",
    "- Use Notebooks only for integrating code from your package and keep complex functionality inside the package. Thus, extract larger bits of code from a notebook and move it into a package or directly develop code in a proper IDE.\n",
    "- Short and Simple is Better: A notebook which generates lots of useful outputs and visuals with a few simple cells is better than a ten page manual. This makes your notebooks more shareable, understandable, and maintainable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Links\n",
    "\n",
    "* https://github.com/NirantK/best-of-jupyter/blob/master/README.md\n",
    "* https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/\n",
    "* https://forums.fast.ai/t/jupyter-notebook-enhancements-tips-and-tricks/17064/16\n",
    "* http://jakevdp.github.io/blog/2013/06/01/ipython-notebook-javascript-python-communication/\n",
    "* https://blog.dominodatalab.com/lesser-known-ways-of-using-notebooks/\n",
    "* https://svds.com/jupyter-notebook-best-practices-for-data-science/\n",
    "* https://github.com/uberwach/leveling-up-jupyter\n",
    "* https://florianwilhelm.info/2018/11/working_efficiently_with_jupyter_lab/\n",
    "* https://www.slideshare.net/katenerush/clean-code-in-jupyter-notebooks\n",
    "* http://jakevdp.github.io/blog/2017/12/05/installing-python-packages-from-jupyter/index.html \n",
    "* https://stackoverflow.com/questions/36427747/scientific-computing-ipython-notebook-how-to-organize-code/38192558#38192558"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "notify_time": "10",
  "toc": {
   "base_numbering": 1,
   "nav_menu": {
    "height": "120px",
    "width": "252px"
   },
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": "block",
   "toc_window_display": false
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  },
  "widgets": {
   "state": {
    "2e3f8bd69ebd4be3919f483f249731fa": {
     "views": [
      {
       "cell_index": 12
      }
     ]
    },
    "4e14b0903c634cb98cd26985125faaba": {
     "views": [
      {
       "cell_index": 12
      }
     ]
    },
    "56737af8a8e3495692f4031787af036a": {
     "views": [
      {
       "cell_index": 12
      }
     ]
    },
    "a1b030b732c141478639c40de30f49e3": {
     "views": [
      {
       "cell_index": 12
      }
     ]
    }
   },
   "version": "1.2.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
